﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Liang.UI
{
    public partial class PictureBoxEx : UserControl
    {
        int maxZoomTimes = 10;

        /// <summary>
        /// 最大放大倍数
        /// </summary>
        public int MaxZoomTimes
        {
            get { return maxZoomTimes; }
            set { maxZoomTimes = value; }
        }

        /// <summary>
        /// 当前放大倍数
        /// </summary>
        int zoomTimes = 1;

        /// <summary>
        /// 当前选取的用于绘制的图片区域
        /// </summary>
        RectangleF srcImageRect = new RectangleF();

        /// <summary>
        /// 目标绘制区域（此区域即窗口大小）
        /// </summary>
        RectangleF dstDrawRect = new RectangleF();

        /// <summary>
        /// 是否正在拖动图片
        /// </summary>
        bool isDragPicture = false;

        /// <summary>
        /// 拖动图片前的鼠标点击坐标
        /// </summary>
        PointF pointDragBefore = new PointF();

        System.Drawing.Image _Image;

        /// <summary>
        /// <para>设置<code>Image</code>时，将会初始化初始绘图区域</para>
        /// </summary>
        public System.Drawing.Image Image
        {
            get
            {
                return _Image;
            }

            set
            {
                _Image = value;
                if (null != value)
                {
                    srcImageRect = new RectangleF(0, 0, value.Width, value.Height);
                    // 新图片，重置操作参数（缩放比例等）
                    zoomTimes = 1;
                    Invalidate();
                }
            }
        }

        public PictureBoxEx()
        {
            InitializeComponent();

            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            Graphics g = pe.Graphics;
            // 清除背景
            using (SolidBrush brush = new SolidBrush(Color.Gray))
            {
                g.FillRectangle(brush, ClientRectangle);
            }

            try
            {
                // 绘制图片
                if (null != Image)
                {
                    RectangleF dstRect = new RectangleF(this.ClientRectangle.X, this.ClientRectangle.Y,
                        this.ClientRectangle.Width, this.ClientRectangle.Height);

                    g.DrawImage(Image, dstRect, this.srcImageRect, GraphicsUnit.Pixel);
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e.ToString());
            }

            base.OnPaint(pe);
        }

        /// <summary>
        /// 鼠标滚轮，用于控制图片缩放
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (0 < e.Delta)
            {
                ZoomIn();
            }
            else
            {
                ZoomOut();
            }

            base.OnMouseWheel(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                // 按下的左键，可以开始拖动图片
                this.isDragPicture = true;
                pointDragBefore = e.Location;
            }

            base.OnMouseDown(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                // 释放了左键，结束图片拖动
                this.isDragPicture = false;
            }

            base.OnMouseUp(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (this.isDragPicture)
            {
                // 正在拖动图片，计算图片的显示区域
                MovePicture(e.Location);
            }

            base.OnMouseMove(e);
        }

        private void PictureBoxEx_MouseEnter(object sender, EventArgs e)
        {
            this.Focus();
        }

        /// <summary>
        /// 放大图片
        /// </summary>
        void ZoomIn()
        {
            if (null == this.Image)
            {
                return;
            }

            if (maxZoomTimes == zoomTimes)
            {
                return;
            }

            SizeF preImageSize = srcImageRect.Size;

            srcImageRect.Width = preImageSize.Width * 0.75f;
            srcImageRect.Height = preImageSize.Height * 0.75f;

            srcImageRect.X += (preImageSize.Width - srcImageRect.Width) / 2;
            srcImageRect.Y += (preImageSize.Height - srcImageRect.Height) / 2;

            zoomTimes++;

            Invalidate();
        }

        /// <summary>
        /// 缩小图片
        /// </summary>
        void ZoomOut()
        {
            if (null == Image)
            {
                return;
            }

            if (1 == this.zoomTimes)
            {
                this.srcImageRect.X = 0;
                this.srcImageRect.Y = 0;
                this.srcImageRect.Width = Image.Width;
                this.srcImageRect.Height = Image.Height;
            }
            else
            {
                SizeF preImageSize = srcImageRect.Size;

                srcImageRect.Width = preImageSize.Width * 1.25f;
                srcImageRect.Height = preImageSize.Height * 1.25f;

                srcImageRect.X -= (srcImageRect.Width - preImageSize.Width) / 2;
                srcImageRect.Y -= (srcImageRect.Height - preImageSize.Height) / 2;

                // 缩小的时候，需考虑图片边界
                if (srcImageRect.X < 0)
                {
                    srcImageRect.X = 0;
                }

                if (srcImageRect.Y < 0)
                {
                    srcImageRect.Y = 0;
                }

                if (Image.Width < (srcImageRect.X + srcImageRect.Width))
                {
                    srcImageRect.X = Image.Width - srcImageRect.Width;
                }

                if (Image.Height < (srcImageRect.Y + srcImageRect.Height))
                {
                    srcImageRect.Y = Image.Height - srcImageRect.Height;
                }

                zoomTimes--;
            }

            Invalidate();
        }

        /// <summary>
        /// 重置图片
        /// </summary>
        void ZoomReset()
        {
            this.srcImageRect = new RectangleF();

            if (null != Image)
            {
                this.srcImageRect.Width = Image.Width;
                this.srcImageRect.Height = Image.Height;
            }

            this.zoomTimes = 1;

            Invalidate();
        }

        void MovePicture(Point p)
        {
            if (null == Image)
            {
                return;
            }

            int nOffSetX = System.Math.Abs(p.X - (int)this.pointDragBefore.X);
            int nOffSetY = System.Math.Abs(p.Y - (int)this.pointDragBefore.Y);

            #region X 方向上的移动
            if (p.X > this.pointDragBefore.X)
            {
                // X 方向上正向拖动，即需要显示拖动起始点左边的图片
                this.srcImageRect.X -= nOffSetX;
                if (this.srcImageRect.X < 0)
                {
                    this.srcImageRect.X = 0;
                }
            }
            else
            {
                // X 方向上反向拖动
                this.srcImageRect.X += nOffSetX;
                if (this.Image.Width < (this.srcImageRect.X + this.srcImageRect.Width))
                {
                    this.srcImageRect.X = this.Image.Width - this.srcImageRect.Width;
                }
            }
            #endregion

            #region Y 方向上的移动

            if (p.Y > this.pointDragBefore.Y)
            {
                // Y 方向上正向拖动
                this.srcImageRect.Y -= nOffSetY;
                if (0 > this.srcImageRect.Y)
                {
                    this.srcImageRect.Y = 0;
                }
            }

            if (p.Y < this.pointDragBefore.Y)
            {
                this.srcImageRect.Y += nOffSetY;
                if (this.Image.Height < (this.srcImageRect.Y + this.srcImageRect.Height))
                {
                    this.srcImageRect.Y = this.Image.Height - this.srcImageRect.Height;
                }
            }
            #endregion

            // 记录最新的拖动之前的坐标
            this.pointDragBefore = p;

            Invalidate();
        }

        private void toolStripMenuItem_zoomIn_Click(object sender, EventArgs e)
        {
            ZoomIn();
        }

        private void toolStripMenuItem_zoomOut_Click(object sender, EventArgs e)
        {
            ZoomOut();
        }

        private void toolStripMenuItem_zoomReset_Click(object sender, EventArgs e)
        {
            ZoomReset();
        }

        private void toolStripMenuItem_Save_Click(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// 窗口上的点到图片上的坐标变换
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public bool WindowPoint2ImagePoint(ref PointF p)
        {
            if (null != Image)
            {
                try
                {
                    p.X = p.X * srcImageRect.Width / ClientRectangle.Width + srcImageRect.X;
                    p.Y = p.Y * srcImageRect.Height / ClientRectangle.Height + srcImageRect.Y;

                    return true;
                }
                catch (Exception e)
                {
                    System.Diagnostics.Debug.WriteLine(e.Message);
                }
            }

            return false;
        }

        /// <summary>
        /// 图片上的点到窗口上的坐标变换
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public bool ImagePoint2WindowPoint(ref PointF p)
        {
            if (null != Image)
            {
                try
                {
                    p.X = (p.X - this.srcImageRect.X) * this.ClientRectangle.Width / this.srcImageRect.Width;
                    p.Y = (p.Y - this.srcImageRect.Y) * this.ClientRectangle.Height / this.srcImageRect.Height;

                    return true;
                }
                catch (Exception e)
                {
                    System.Diagnostics.Debug.WriteLine(e.Message);
                }
            }

            return false;
        }
    }
}
